home *** CD-ROM | disk | FTP | other *** search
/ Nebula 2 / Nebula Two.iso / SourceCode / MiscKit1.7.1 / MiscKit / Source / MiscGISKit / MiscIrelandCoordConverter.m < prev    next >
Text File  |  1995-07-20  |  9KB  |  269 lines

  1. /*====================== MiscIrelandCoordConverter.m ========================*/
  2. /* MiscIrelandCoordConverter class supports the calculations required for
  3.    converting between World and the Irish version of the Universal Transverse
  4.    Mercator coordinates.
  5.  
  6.    There is only one instance ever, so unless changes are made, this class
  7.    is NON REENTRANT.
  8.  
  9.    For information on the underlying mathematics, refer to:
  10.  
  11.      1- Ordinance Survey Information, "Transverse Mercator Projection,
  12.         Constants, Formula and Methods", March 1983
  13.  
  14.      2- Ordinance Survey, "Tables for the Transverse Mercator Projection
  15.         of Ireland", 1953, reprinted 1971
  16.  
  17. NOTES:
  18.     - Irish document uses old tables and some constants to keep
  19.       values small. This has no effect here.
  20.  
  21.     - UK document has IIIA and XIIA equation, ie extra correction terms,
  22.       which are not used on Irish Grid and are left out here.
  23.  
  24.     - UK document cancels the -'s in V and VI and the -'s in the E
  25.       equation, which the Irish Grid does not do. This has no effect.
  26.  
  27.     - Irish document last term in VI is an extra one not present in UK
  28.       but is included here.
  29.  
  30.     - There is an erratum to the Irish document that gives the new
  31.       values of a and b. Old values are in the IrishGridOldUTMConstants,
  32.       new ones are in the IrishGridUTMConstants object.
  33.  
  34.     - Accuracy is difficult to compare with the example values in the
  35.       document because they are derived from tables with interpolation.
  36.       Our calculated values are within .1 meter of the one from the
  37.       table, and it is probably due to the fact that the tabular examples
  38.       do not use "second differences". In other words, the calculated
  39.       values are probably more correct to more places than the book
  40.       example values.
  41.  
  42. IMPORTANT: These equations are accurate to within 1 millimeter. Calculations
  43.        requiring greater accuracy must use formulae in:
  44.  
  45.         Redfern, JCB, "Transverse Mercator Formulae", 1948,
  46.         Empire Survey Review, 9(69) pg318-322
  47.  
  48.         Extra decimal places are stored only for the purpose of 
  49.         slowing error propagation that affects the numbers at the
  50.         millimeter scale, not because they are meaningful in and of 
  51.         themselves.
  52.  
  53.    DMA Release 0.8, Copyright @1993 by Genesis Project, Ltd. All Rights
  54.    Reserved. For further information on terms and conditions see:
  55.         Documentation/GISKit/Agreements-Legal-README
  56.  
  57. HISTORY
  58. 12-Mar-93  Dale Amon at GPL
  59.        Split UTMGrid into constants and convertor parts.
  60. 22-Feb-93  Dale Amon at GPL
  61.        Created.
  62. */
  63.  
  64. #import <math.h>
  65. #import <misckit/miscgiskit.h>
  66.  
  67. @implementation MiscIrelandCoordConverter
  68.  
  69.  
  70. /*---------------------------------------------------------------------------*/
  71. /* Calculate latitude and longitude given grid N and E. Accurate to 1 mm.
  72.    Uses constants:
  73.     lambda0 = longitude of grid origin
  74. */
  75.  
  76. - (void) utmToWorld
  77. {    double    E,N;
  78.     double    phiPrime;
  79.     double    y,y2,y3,y4,y5,y6;
  80.     double    nu2,nu3,nu4,nu5,nu7;
  81.     double    tanPhiPrime,tan2PhiPrime,tan4PhiPrime,tan6PhiPrime;
  82.     double    secPhiPrime;
  83.     double    VII, VIII,IX, X, XI, XII;
  84.  
  85.     /* conversion is driven by the source constants */
  86.     xlate = srcConstants;
  87.  
  88.     E = src[MISC_EASTINGS];
  89.     N = src[MISC_NORTHINGS];
  90.  
  91.     phiPrime = [self calcPhiPrime: N];
  92.  
  93.     /* precalculate nu, rho and etaSqrd and powers of nu */
  94.     [self blackboardCalc: phiPrime];
  95.     nu2   = nu*nu;
  96.     nu3   = nu2*nu;
  97.     nu4   = nu2*nu2;
  98.     nu5   = nu3*nu2;
  99.     nu7   = nu4*nu3;
  100.  
  101.     /* precalculate all the trig values */
  102.  
  103.     tanPhiPrime  = tan(phiPrime);
  104.     tan2PhiPrime = tanPhiPrime  * tanPhiPrime;
  105.     tan4PhiPrime = tan2PhiPrime * tan2PhiPrime;
  106.     tan6PhiPrime = tan4PhiPrime * tan2PhiPrime;
  107.     secPhiPrime  = 1/cos(phiPrime);
  108.  
  109.     /* precalculate all the powers of delta E */
  110.     y  = E - xlate->E0;
  111.     y2 = y*y;
  112.     y3 = y2*y;
  113.     y4 = y2*y2;
  114.     y5 = y3*y2;
  115.     y6 = y3*y3;
  116.  
  117.     /* Calculate the terms used by the latitude and longitude equations */
  118.  
  119.     VII  = tanPhiPrime/(2.0*rho*nu);
  120.     VIII = tanPhiPrime/(24.0*rho*nu3) *
  121.         (5.0 + 3.0*tan2PhiPrime + etaSqrd - 9.0*etaSqrd*tan2PhiPrime);
  122.     IX   = tanPhiPrime/(720.0*rho*nu5) *
  123.         (61.0 + 90.0*tan2PhiPrime + 45.0*tan4PhiPrime);
  124.  
  125.     X    = secPhiPrime/nu;
  126.     XI   = secPhiPrime/(6.0*nu3) * (nu/rho + 2.0*tan2PhiPrime);
  127.     XII  = secPhiPrime/(120.0*nu5) *
  128.         (5.0 + 28.0*tan2PhiPrime + 24.0*tan4PhiPrime);
  129.  
  130.     /* and finally, we give you the latitude and the longitude */    
  131.     dst[MISC_LATITUDE]  = phiPrime     - y2*VII + y4*VIII + y6*IX;
  132.     dst[MISC_LONGITUDE] = xlate->lambda0 + y*X - y3*XI + y5*XII;
  133.     dst[MISC_ALTITUDE]  = src[MISC_ELEVATION];
  134. }
  135.  
  136.  
  137. /*---------------------------------------------------------------------------*/
  138. /* Given World Coordinates, calculate the local grid coordinates in
  139.    a UTM projection.
  140.    Uses constants:
  141.     N0,E0 = True origin offset from grid False origin
  142.     lambda0 = longitude of True origin. 
  143. */
  144.  
  145. - (void) worldToUTM
  146. {    double    phi,lambda;
  147.     double    cosPhi,cos2Phi,cos3Phi,cos5Phi;
  148.     double    tanPhi,tan2Phi,tan4Phi;
  149.     double    P1, P2, P3, P4, P5;
  150.     double    I,II,III,IV,V,VI;
  151.  
  152.     /* conversion is driven by the destination constants */
  153.     xlate = dstConstants;
  154.  
  155.     /* assumes the internal storage format is double precision radians */
  156.     phi = src[MISC_LATITUDE]; lambda = src[MISC_LONGITUDE];
  157.  
  158.     /* Calculate the Developed Arc of a Meridian from phi to the
  159.        True Origin. */
  160.     [self calcM: phi];
  161.  
  162.     /* calculate nu, rho and etaSqrd */
  163.     [self blackboardCalc: phi];
  164.  
  165.     /* precalculate all the trig values that are not on blackboard */
  166.     cosPhi  = cos(phi);
  167.     cos2Phi = cosPhi*cosPhi;
  168.     cos3Phi = cos2Phi*cosPhi;
  169.     cos5Phi = cos2Phi*cos3Phi;
  170.  
  171.     tanPhi  = tan(phi);
  172.     tan2Phi = tanPhi  * tanPhi;
  173.     tan4Phi = tan2Phi * tan2Phi;
  174.  
  175.     /* Precalculate all the powers of delta lambda */
  176.     P1 = lambda - xlate->lambda0;
  177.     P2 = P1 * P1;
  178.     P3 = P2 * P1;
  179.     P4 = P2 * P2;
  180.     P5 = P3 * P2;
  181.  
  182.     /* Calculate the terms used by the Easting and Northing equations */
  183.  
  184.     I   = M + xlate->N0;
  185.     II  = nu/2.0   * sinPhi * cosPhi; 
  186.     III = nu/24.0  * sinPhi * cos3Phi * (5.0 - tan2Phi + 9.0 * etaSqrd);
  187.     IV  = nu       * cosPhi;
  188.     V   = nu/6.0   * cos3Phi * (nu/rho - tan2Phi);
  189.  
  190.     /* The Irish equation has one extra term in VI that the UK
  191.        and UTM to not have. */
  192.     VI  = nu/120.0 * cos5Phi * 
  193.         (5.0 - 18.0*tan2Phi + tan4Phi + 14.0*etaSqrd - 
  194.          58.0 * etaSqrd * tan2Phi +  2.0 * etaSqrd * tan4Phi);
  195.  
  196.     /* And finally, we calculate the local grid coordinates */
  197.     dst[MISC_NORTHINGS] = I         + P2*II + P4*III;
  198.     dst[MISC_EASTINGS]  = xlate->E0 + P1*IV + P3*V   + P5*VI;
  199.     dst[MISC_ELEVATION] = src[MISC_ALTITUDE];
  200.  }
  201.  
  202.  
  203. /*===========================================================================*/
  204. /* Class methods */
  205. /*===========================================================================*/
  206. /* Only one converter of this type is every needed. Of course if we got
  207.    into a really big multiprocess agora system there might be a case
  208.    for multiple converters. Since this object is shared by many, it
  209.    doesn't particularly matter what zone it is allocated from.
  210.  
  211.    Note that we use a secret allocation routine. The parent class blocks the
  212.    normal alloc's because it also creates only a single instance.
  213. */
  214.  
  215. static id theIrelandCoordConverter = nil;
  216.  
  217. + new
  218. {
  219.     if (!theIrelandCoordConverter)
  220.     theIrelandCoordConverter = [[super superalloc] init];
  221.     return theIrelandCoordConverter;
  222. }
  223.  
  224.  
  225. /*===========================================================================*/
  226. /* Initialization methods */
  227. /*===========================================================================*/
  228. /* We add a list of all the services which we are able to provide.
  229.    Note that there is only one MiscIrelandCoordConverter ever created. Once 
  230.    initialized the same object is given to all comers and can not be destroyed.
  231.  
  232.    Simple copies when the class is the same is okay because we know that
  233.    by definition the UTM constants are be the same.
  234.  
  235. */
  236.  
  237. - init
  238.   { Class    world, utm, oldutm;
  239.  
  240.     [super init];
  241.     world     = [MiscWorldCoord         class];
  242.     utm       = [MiscIrelandUTMCoord    class];
  243.     oldutm    = [MiscIrelandOldUTMCoord class];
  244.  
  245.     [self addService: @selector(utmToWorld) convertsFrom: utm    to: world]; 
  246.     [self addService: @selector(worldToUTM) convertsFrom: world  to: utm]; 
  247.     [self addService: @selector(utmToWorld) convertsFrom: oldutm to: world]; 
  248.     [self addService: @selector(worldToUTM) convertsFrom: world  to: oldutm]; 
  249.     [self addService: @selector(copyCoords) convertsFrom: world  to: world]; 
  250.     [self addService:[self fastCopySelector] convertsFrom: utm   to: utm]; 
  251.     [self addService:[self fastCopySelector] convertsFrom:oldutm to: oldutm];
  252.  
  253.     return self;
  254.   }
  255.  
  256.  
  257. /*===========================================================================*/
  258. /* Archive methods */
  259. /*===========================================================================*/
  260.  
  261. - finishUnarchiving
  262.  {
  263.     [self free];
  264.     return [MiscIrelandCoordConverter new];
  265.  }
  266.  
  267.  
  268. @end
  269.